#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

typedef struct {
    int    pid;
    int   state;
    char   value[20];
} process_info;

void handleSignal();

const int FREE = 0;
const int WORKING = 1;
int PROCESS_COUNT = 5;

void main(int argc, char *argv[]) {
    volatile process_info *processes;

    int pid, i, shmid, shmflg, permflg, res; 
    void *map_adr;

    permflg = (SHM_R|SHM_W);
    shmflg = IPC_CREAT | IPC_EXCL | permflg;
    shmid = shmget(IPC_PRIVATE, PROCESS_COUNT * sizeof(process_info), shmflg);
    map_adr = shmat(shmid, NULL, 0);

    processes = (process_info *) map_adr;

    for (i=0;i<PROCESS_COUNT;i++) {
        printf("PRE_FORK\n");
        pid=fork();
        if (pid==0) {
            int _pid = getpid();
            sigset_t set;
            int sig;
            sigemptyset(&set);
            sigaddset(&set, SIGUSR1);

            struct sigaction act;
            memset(&act, 0, sizeof(act));
            act.sa_handler = handleSignal; 
            sigaction(SIGUSR1, &act, 0); //need to activate SIGUSR1
            map_adr = shmat(shmid, NULL, 0); //get shared memory in child processesess
            while (1) {
                sigwait(&set, &sig);

                int iter, index;
                for (iter = 0; iter < PROCESS_COUNT; iter++) {
                    if (processes[iter].pid == _pid) {
                        index = iter;
                        break;
                    }
                }
                sleep(2); //some long work
                printf("I work! Pid=%d. Value=%s\n", _pid, processes[index].value);
                processes[index].state = FREE;
            } 
        } else {
            processes[i].pid = pid; //init processesess pool
            processes[i].state = FREE;
        }
    }

    //pause to set signal handlers of processesesses
    sleep(3);

    char values_to_process[20][20];
    for (i=0; i<20; i++) {
        sprintf(values_to_process[i], "value %d", i);
    }

    //main work loop
    int process_iter;
    for (i=0; i<20; i++) {
        int served = 0;
        do {
            for (process_iter=0; process_iter<PROCESS_COUNT; process_iter++) {
                if (processes[process_iter].state == FREE) {
                    processes[process_iter].state = WORKING;
                    memcpy(processes[process_iter].value, values_to_process[i], sizeof(values_to_process[i]));
                    kill(processes[process_iter].pid, SIGUSR1);
                    served = 1;
                    break;
                }
            }
        } while (served == 0);
    }

    //wait for all jobs become done
    sleep(3);
    //kill all child processesesses to avoid zombies
    for (i=0; i<PROCESS_COUNT; i++) {
        kill(processes[i].pid, SIGKILL);
    }

    //release shared memory
    res = shmdt(map_adr);
    res = shmctl(shmid, IPC_RMID, NULL);

    exit(0);
}

//dummy implementation of signal-handler
//needed for activate SIGUSR1 via sigaction function
void handleSignal(int signo) {
    printf("process handled signal %d\n", signo);
}

